﻿using Savvy.HTMLHelperExensions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Web.Mvc;
using System.Web.Routing;
using System.Web;
using System;
using Moq;
using System.Collections.Generic;
using Savvy.TestExtensions;

namespace Savvy.HTMLHelperExtensions.Test
{
    
    
    /// <summary>
    ///This is a test class for HTMLHelperExtensionsTest and is intended
    ///to contain all HTMLHelperExtensionsTest Unit Tests
    ///</summary>
  [TestClass()]
  public class HTMLHelperExtensionsTest
  {


    private TestContext testContextInstance;

    /// <summary>
    ///Gets or sets the test context which provides
    ///information about and functionality for the current test run.
    ///</summary>
    public TestContext TestContext
    {
      get
      {
        return testContextInstance;
      }
      set
      {
        testContextInstance = value;
      }
    }

    #region Additional test attributes
    // 
    //You can use the following additional attributes as you write your tests:
    //
    //Use ClassInitialize to run code before running the first test in the class
    //[ClassInitialize()]
    //public static void MyClassInitialize(TestContext testContext)
    //{
    //}
    //
    //Use ClassCleanup to run code after all tests in a class have run
    //[ClassCleanup()]
    //public static void MyClassCleanup()
    //{
    //}
    //
    //Use TestInitialize to run code before running each test
    //[TestInitialize()]
    //public void MyTestInitialize()
    //{
    //}
    //
    //Use TestCleanup to run code after each test has run
    //[TestCleanup()]
    //public void MyTestCleanup()
    //{
    //}
    //
    #endregion


    #region Tests for default identifying properties
    [TestMethod()]
    public void ActionLinkTest_single_model_instance_with_defaults_only_no_attributes()
    {
      var modelInstance = new TestType();

      HtmlHelper htmlHelper = GetHtmlHelper();

      Assert.AreEqual(
        String.Format(@"<a href=""" + AppPathModifier + @"/app/home/Details/{0}"">linktext</a>", modelInstance.ID),
        htmlHelper.ActionLink("linktext", modelInstance)
        );
    }

    [TestMethod()]
    public void ActionLinkTest_should_get_default_with_Id()
    {
      var modelInstance = new TestTypeWithId();

      HtmlHelper htmlHelper = GetHtmlHelper();

      Assert.AreEqual(
        String.Format(@"<a href=""" + AppPathModifier + @"/app/home/Details/{0}"">linktext</a>", modelInstance.Id),
        htmlHelper.ActionLink("linktext", modelInstance)
        );
    }

    [TestMethod()]
    public void ActionLinkTest_should_get_default_with_id()
    {
      var modelInstance = new TestTypeWithid();

      HtmlHelper htmlHelper = GetHtmlHelper();

      Assert.AreEqual(
        String.Format(@"<a href=""" + AppPathModifier + @"/app/home/Details/{0}"">linktext</a>", modelInstance.id),
        htmlHelper.ActionLink("linktext", modelInstance)
        );
    }

    [TestMethod()]
    public void ActionLinkTest_should_get_default_with__ID()
    {
      var modelInstance = new TestTypeWith_ID();

      HtmlHelper htmlHelper = GetHtmlHelper();

      Assert.AreEqual(
        String.Format(@"<a href=""" + AppPathModifier + @"/app/home/Details/{0}"">linktext</a>", modelInstance._ID),
        htmlHelper.ActionLink("linktext", modelInstance)
        );
    }

    [TestMethod()]
    public void ActionLinkTest_should_get_default_with__Id()
    {
      var modelInstance = new TestTypeWith_Id();

      HtmlHelper htmlHelper = GetHtmlHelper();

      Assert.AreEqual(
        String.Format(@"<a href=""" + AppPathModifier + @"/app/home/Details/{0}"">linktext</a>", modelInstance._Id),
        htmlHelper.ActionLink("linktext", modelInstance)
        );
    }

    [TestMethod()]
    public void ActionLinkTest_should_get_default_with__id()
    {
      var modelInstance = new TestTypeWith_id();

      HtmlHelper htmlHelper = GetHtmlHelper();

      Assert.AreEqual(
        String.Format(@"<a href=""" + AppPathModifier + @"/app/home/Details/{0}"">linktext</a>", modelInstance._id),
        htmlHelper.ActionLink("linktext", modelInstance)
        );
    }
    #endregion

    [TestMethod()]
    public void ActionLinkTest_should_get_id_from_property_attribute()
    {
      var modelInstance = new TestTypeWithAlternateID();

      HtmlHelper htmlHelper = GetHtmlHelper();

      Assert.AreEqual(
        String.Format(@"<a href=""" + AppPathModifier + @"/app/home/Details/{0}"">linktext</a>", modelInstance.AlternateID),
        htmlHelper.ActionLink("linktext", modelInstance)
        );
    }

    [TestMethod()]
    public void ActionLinkTest_should_throw_exception_if_model_has_no_id_property()
    {
      var modelInstance = new TestTypeWithoutID();

      HtmlHelper htmlHelper = GetHtmlHelper();
      
      AssertExtensions.AssertThrows(
        typeof(NonInferrableModelIDException),
        () => { htmlHelper.ActionLink("linktext", modelInstance); }
      );
    }

    [TestMethod()]
    public void ActionLinkTest_should_get_controller_name_from_class_attribute()
    {
      var modelInstance = new TestTypeWithControllerName();

      HtmlHelper htmlHelper = GetHtmlHelper();

      Assert.AreEqual(
        String.Format(@"<a href=""" + AppPathModifier + @"/app/OtherController/Details/{0}"">linktext</a>", modelInstance.ID),
        htmlHelper.ActionLink("linktext", modelInstance)
        );
    }

    [TestMethod()]
    public void ActionLinkTest_should_get_index_method_name_from_class_attribute()
    {
      var modelInstance = new List<TestTypeWithIndexMethodName> { new TestTypeWithIndexMethodName() };

      HtmlHelper htmlHelper = GetHtmlHelper();

      Assert.AreEqual(
        String.Format(@"<a href=""" + AppPathModifier + @"/app/home/OtherIndex"">linktext</a>"),
        htmlHelper.ActionLink("linktext", modelInstance)
        );
    }

    [TestMethod()]
    public void ActionLinkTest_should_get_details_method_name_from_class_attribute()
    {
      var modelInstance = new TestTypeWithDetailsMethodName();

      HtmlHelper htmlHelper = GetHtmlHelper();

      Assert.AreEqual(
        String.Format(@"<a href=""" + AppPathModifier + @"/app/home/OtherDetails/123"">linktext</a>"),
        htmlHelper.ActionLink("linktext", modelInstance)
        );
    }

    #region Blatantly pilfered test code from the ASP.NET MVC source
    internal static HtmlHelper GetHtmlHelper()
    {
      HttpContextBase httpcontext = GetHttpContext("/app/", null, null);
      RouteCollection rt = new RouteCollection();
      rt.Add(new Route("{controller}/{action}/{id}", null) { Defaults = new RouteValueDictionary(new { id = "defaultid" }) });
      rt.Add("namedroute", new Route("named/{controller}/{action}/{id}", null) { Defaults = new RouteValueDictionary(new { id = "defaultid" }) });
      RouteData rd = new RouteData();
      rd.Values.Add("controller", "home");
      rd.Values.Add("action", "oldaction");

      ViewDataDictionary vdd = new ViewDataDictionary();

      Mock<ViewContext> mockViewContext = new Mock<ViewContext>();
      mockViewContext.Expect(c => c.HttpContext).Returns(httpcontext);
      mockViewContext.Expect(c => c.RouteData).Returns(rd);
      mockViewContext.Expect(c => c.ViewData).Returns(vdd);
      Mock<IViewDataContainer> mockVdc = new Mock<IViewDataContainer>();
      mockVdc.Expect(vdc => vdc.ViewData).Returns(vdd);

      HtmlHelper htmlHelper = new HtmlHelper(mockViewContext.Object, mockVdc.Object, rt);
      return htmlHelper;
    }

    public const string AppPathModifier = "/TEST_APP_MODIFIER";

    public static HttpContextBase GetHttpContext(string appPath, string requestPath, string httpMethod)
    {
      return GetHttpContext(appPath, requestPath, httpMethod, Uri.UriSchemeHttp.ToString(), -1);
    }

    public static HttpContextBase GetHttpContext(string appPath, string requestPath, string httpMethod, string protocol, int port)
    {
      Mock<HttpContextBase> mockHttpContext = new Mock<HttpContextBase>();

      if (!String.IsNullOrEmpty(appPath))
      {
        mockHttpContext.Expect(o => o.Request.ApplicationPath).Returns(appPath);
      }
      if (!String.IsNullOrEmpty(requestPath))
      {
        mockHttpContext.Expect(o => o.Request.AppRelativeCurrentExecutionFilePath).Returns(requestPath);
      }

      Uri uri;

      if (port >= 0)
      {
        uri = new Uri(protocol + "://localhost" + ":" + Convert.ToString(port));
      }
      else
      {
        uri = new Uri(protocol + "://localhost");
      }
      mockHttpContext.Expect(o => o.Request.Url).Returns(uri);

      mockHttpContext.Expect(o => o.Request.PathInfo).Returns(String.Empty);
      if (!String.IsNullOrEmpty(httpMethod))
      {
        mockHttpContext.Expect(o => o.Request.HttpMethod).Returns(httpMethod);
      }

      mockHttpContext.Expect(o => o.Session).Returns((HttpSessionStateBase)null);
      mockHttpContext.Expect(o => o.Response.ApplyAppPathModifier(It.IsAny<string>())).Returns<string>(r => AppPathModifier + r);
      return mockHttpContext.Object;
    }
    #endregion



    #region Types with default ID's
    class TestType
    {
      public TestType()
      {
        ID = 123;
      }

      public int ID { get; set; }
    }

    class TestTypeWithId
    {
      public TestTypeWithId()
      {
        Id = 123;
      }

      public int Id { get; set; }
    }

    class TestTypeWithid
    {
      public TestTypeWithid()
      {
        id = 123;
      }

      public int id { get; set; }
    }

    class TestTypeWith_ID
    {
      public TestTypeWith_ID()
      {
        _ID = 123;
      }

      public int _ID { get; set; }
    }

    class TestTypeWith_Id
    {
      public TestTypeWith_Id()
      {
        _Id = 123;
      }

      public int _Id { get; set; }
    }

    class TestTypeWith_id
    {
      public TestTypeWith_id()
      {
        _id = 123;
      }

      public int _id { get; set; }
    } 
    #endregion

    class TestTypeWithAlternateID
    {
      public TestTypeWithAlternateID()
      {
        AlternateID = 321;
      }

      [HTMLHelperIdentifier]
      public int AlternateID { get; set; }
    }

    [HTMLHelperControllerName("OtherController")]
    class TestTypeWithControllerName
    {
      public TestTypeWithControllerName()
      {
        ID = 123;
      }

      public int ID { get; set; }
    }

    [HTMLHelperIndexMethodName("OtherIndex")]
    class TestTypeWithIndexMethodName
    {
      public TestTypeWithIndexMethodName()
      {
        ID = 123;
      }

      public int ID { get; set; }
    }

    [HTMLHelperDetailsMethodName("OtherDetails")]
    class TestTypeWithDetailsMethodName
    {
      public TestTypeWithDetailsMethodName()
      {
        ID = 123;
      }

      public int ID { get; set; }
    }

    class TestTypeWithoutID
    {
    }
  }
}
